import React from "react";
import { Formik, Form } from "formik";
import { useDropzone } from 'react-dropzone';
import { useSelector, useDispatch } from 'react-redux';
import { setUserData } from "../../../store/slices/userSlice";
import { FormikField } from "../../../components/FormikField/FormikField";
import { validationProfileSchema } from "./Profile.validation";
import { getUserSelector } from "../../../store/selectors/selectUserData";
import userServices from "../../../services/userServices";
import authServices from "../../../services/authServices";
import CardRow from "../../CardRow/CardRow";
import { ReactComponent as UserIcon } from '../../../images/no_profile.svg';
import Button from "../../../shared/ui/Button/Button";
import { StyledLabelCard } from "../LabelCard.styled";
import { StyledHR } from "../../../components/HorizontalRule.styled";
import { StyledProfileBoldText } from "./BoldText.styled";
import ContainerCard from "../../ContainerCard/ContainerCard";
import styles from './Profile.module.scss';
import buttonStyles from '../ButtonStyle.module.scss';
import Textfield from "../../../shared/ui/Textfield/Textfield";
import { useOnClickOutside } from "../../../shared/ui/hooks/useOnClickOutside";
import { ERowSateAction, IRowState, IRowStateAction, TChangeInput } from "./Profile.types";

const reducer = (state: IRowState, action: IRowStateAction) => {
    switch (action.type) {
        case ERowSateAction.USERNAME:
            return { ...state, isDisplayedUserName: action.payload };
        case ERowSateAction.FIRSTNAME:
            return { ...state, isDisplayedFirstName: action.payload };
        case ERowSateAction.MIDDLENAME:
            return { ...state, isDisplayedMiddleName: action.payload };
        case ERowSateAction.LASTNAME:
            return { ...state, isDisplayedLastName: action.payload };
        case ERowSateAction.EMAIL:
            return { ...state, isDisplayedEmail: action.payload };
        default:
            return state;
    }
};

const initialState: IRowState = {
    isDisplayedUserName: true,
    isDisplayedFirstName: true,
    isDisplayedMiddleName: true,
    isDisplayedLastName: true,
    isDisplayedEmail: true
}

/**
 * @function ProfileCard - Отображает компонент карточки профиля с аватаром, полями имени и email.
 * @returns { React.JSX.Element } - Компонент карточки профиля. 
 */
const ProfileCard: React.FC = (): React.JSX.Element => {

    const [rowState, dispatchRowState] = React.useReducer(reducer, initialState);

    /**
     * Ссылка на неотображаемый input для ввода изображения при клике на кнопку.
     */
    const fileInputRef = React.useRef<HTMLInputElement>(null);

    /**
     * Ссылка на поле ввода отображаемого имени.
     */
    const usernameInputRef = React.useRef<HTMLInputElement>(null);

    /**
     * Ссылка на поле ввода email.
     */
    const emailInputRef = React.useRef<HTMLInputElement>(null);

    /**
     * Ссылка на поле ввода фамилии.
     */
    const lastnameInputRef = React.useRef<HTMLInputElement>(null);

    /**
     * Ссылка на поле ввода имени.
     */
    const firstnameInputRef = React.useRef<HTMLInputElement>(null);

    /**
     * Ссылка на поле ввода отчества.
     */
    const middlenameInputRef = React.useRef<HTMLInputElement>(null);

    /**
     * @function onClickOutside - Обрабатывает клик вне поля ввода.
     * @param { React.RefObject<any> | React.MutableRefObject<any> } reference - Ссылка на поле ввода.
     * @param { React.Dispatch<IRowStateAction> } setStateElement - Функция установки состояния
     *  поля ввода.
     * @param { boolean } elementState - Состояние поля ввода.
     */
    const onClickOutside = (
        stateAction: ERowSateAction,
        reference: React.RefObject<any> | React.MutableRefObject<any>,
        setStateElement: React.Dispatch<IRowStateAction>,
        elementState: boolean
    ) => {
        useOnClickOutside({
            ref: reference,
            onClickOutside: () => {
                setStateElement({
                    type: stateAction,
                    payload: !elementState
                });
            },
            enabled: true
        });
    };

    onClickOutside(
        ERowSateAction.USERNAME,
        usernameInputRef,
        dispatchRowState,
        rowState.isDisplayedUserName
    );

    onClickOutside(
        ERowSateAction.FIRSTNAME,
        firstnameInputRef,
        dispatchRowState,
        rowState.isDisplayedFirstName
    );

    onClickOutside(
        ERowSateAction.MIDDLENAME,
        middlenameInputRef,
        dispatchRowState,
        rowState.isDisplayedMiddleName
    );

    onClickOutside(
        ERowSateAction.LASTNAME,
        lastnameInputRef,
        dispatchRowState,
        rowState.isDisplayedLastName
    );

    onClickOutside(
        ERowSateAction.EMAIL,
        emailInputRef,
        dispatchRowState,
        rowState.isDisplayedEmail
    );

    /**
     * Получение сохраненных в хранилище redux данных пользователя.
     */
    const userData = useSelector(getUserSelector);

    /**
     * Функция для записи данных пользователя в хранилище redux.
     */
    const dispatch = useDispatch();

    /**
     * @function handleAvatarButtonClick - Обработчик нажатия на кнопку изменения аватара.
     *  Вызывает клик на скрытом intut.
     * 
     * @returns { void }
     */
    const handleAvatarButtonClick = () => {
        fileInputRef.current.click();
    }

    /**
    * @function handleUserNameButtonClick - Обработчик нажатия на кнопку изменения данных в поле, либо 
    * нажатия клавиши Enter в поле вода. Открывает/закрывает поле ввода. Устанавливает состояние каждой
    * строки на основе переданного действия.
    * @param { ERowSateAction } rowAction -  Действие для поля ввода.
    * @returns { void }
    */
    const handleRowDataInput = (rowAction: ERowSateAction) => {

        dispatchRowState({
            type: ERowSateAction.USERNAME,
            payload: (rowAction === ERowSateAction.USERNAME) ? !rowState.isDisplayedUserName : true
        });
        dispatchRowState({
            type: ERowSateAction.EMAIL,
            payload: (rowAction === ERowSateAction.EMAIL) ? !rowState.isDisplayedEmail : true
        });
        dispatchRowState({
            type: ERowSateAction.FIRSTNAME,
            payload: (rowAction === ERowSateAction.FIRSTNAME) ? !rowState.isDisplayedFirstName : true
        });
        dispatchRowState({
            type: ERowSateAction.MIDDLENAME,
            payload: (rowAction === ERowSateAction.MIDDLENAME) ? !rowState.isDisplayedMiddleName : true
        });
        dispatchRowState({
            type: ERowSateAction.LASTNAME,
            payload: (rowAction === ERowSateAction.LASTNAME) ? !rowState.isDisplayedLastName : true
        });
    }

    /**
     * @function handleChangeImage - Обработчик смены изображения аватара. Принимает либо событие изменения
     *  поля ввода, либо массив файлов. Отсылает запрос на смену изображения. Сохраняет ответ в avatar.
     * @param { TChangeInput } props - Передаваемые параметры.
     * @returns { void } 
     */
    const handleChangeImage = async (props: TChangeInput) => {

        let file: File;

        if ('target' in props) {
            const newProps = props as React.ChangeEvent<HTMLInputElement>;
            // eslint-disable-next-line prefer-destructuring
            file = newProps.target.files[0];
        } else {
            const newProps = props as File[];
            // eslint-disable-next-line prefer-destructuring
            file = newProps[0];
        }

        const formData = new FormData();
        formData.append('file', file);

        try {
            const response = await userServices.setAvatar(formData);
            dispatch(setUserData(response));

        } catch (error) {
            console.error(error);
        }
    }

    /**
     * Возвращает объект, содержащий свойства для корневого элемента и поля выбора файла для dropzone.
     * 
     * @param { Function } - Функция обратного вызова для обработки изменения изображения.
     * @returns { Object } -  Объект, содержащий свойства для корневого элемента и поля выбора файла.
     */
    const { getRootProps, getInputProps } = useDropzone({
        onDrop: handleChangeImage
    });

    /**
     * @function renderAvatar - Рендерит либо установленный аватар, либо анонимный,
     *  если он не был установлен ранее. 
     * @returns { React.ReactElement } - Елемент с аватаром.
     */
    const renderAvatar = (): React.ReactElement => (
        <div
            className={userData.avatar ? styles.userAvatar : styles.noUser}
            {...getRootProps()}
        >
            {userData.avatar ? <img src={`/uploads/${userData.avatar}`} alt="avatar" /> : <UserIcon />}
        </div>
    );

    return (
        <div className={styles.profileCard} id="profileCard">
            <StyledLabelCard>Профиль</StyledLabelCard>
            <ContainerCard>
                <input
                    className={styles.fileInput}
                    type="file"
                    onChange={handleChangeImage}
                    ref={fileInputRef}
                />
                <input
                    className={styles.fileInput}
                    type="file"
                    {...getInputProps()}
                />
                <CardRow
                    label="Аватар"
                    content={renderAvatar()}
                    action={
                        <Button
                            className={buttonStyles.buttonStyle}
                            onClick={handleAvatarButtonClick}
                        >
                            Добавить
                        </Button>
                    }
                />
                <StyledHR />
                <Formik
                    validationSchema={validationProfileSchema}
                    initialValues={{
                        username: userData.username,
                        email: userData.email,
                        lastname: userData.last_name,
                        firstname: userData.first_name,
                        middlename: userData.middle_name
                    }}
                    onSubmit={
                        async (values) => {

                            try {
                                const profile = await authServices.updateUser({
                                    last_name: values.lastname,
                                    first_name: values.firstname,
                                    middle_name: values.middlename,
                                    username: values.username,
                                    email: values.email,
                                    role_id: userData.role_id
                                });

                                dispatch(setUserData(profile));

                            } catch (error) {
                                console.error(error);
                            }
                        }
                    }
                >
                    {({ handleSubmit, errors }) => (
                        <Form className={styles.form} onSubmit={(event) => {
                            event.preventDefault();
                        }}>
                            <CardRow
                                label="Отображаемое имя"
                                content={
                                    rowState.isDisplayedUserName
                                        ?
                                        <StyledProfileBoldText onClick={(event) => {
                                            event.stopPropagation();
                                            handleRowDataInput(ERowSateAction.USERNAME);
                                        }}>
                                            {userData.username}
                                        </StyledProfileBoldText>
                                        :
                                        <FormikField name="username" errorClassName={styles.textFieldError}>
                                            <Textfield
                                                marginLeft="8vw"
                                                autoFocus
                                                type="text"
                                                inputWidth="20rem"
                                                ref={usernameInputRef}
                                                onKeyDown={event => {
                                                    event.stopPropagation();
                                                    if (event.key === 'Enter') {
                                                        event.preventDefault();
                                                        if (!errors.username) {
                                                            handleRowDataInput(ERowSateAction.USERNAME);
                                                        }                                                      
                                                        if (!rowState.isDisplayedUserName) {
                                                            handleSubmit();
                                                        }
                                                    }
                                                }}
                                            />
                                        </FormikField>
                                }
                                action={
                                    <Button
                                        type="submit"
                                        onClick={(event) => {
                                            event.stopPropagation();
                                            if (!errors.username || rowState.isDisplayedUserName) {
                                                handleRowDataInput(ERowSateAction.USERNAME);
                                            }                                         
                                            if (!rowState.isDisplayedUserName) {
                                                handleSubmit();
                                            }
                                        }}
                                        className={buttonStyles.buttonStyle}
                                    >
                                        {rowState.isDisplayedUserName ? 'Изменить' : 'Сохранить'}
                                    </Button>
                                }
                            />
                            <StyledHR />
                            <CardRow
                                label="Фамилия"
                                content={
                                    rowState.isDisplayedLastName
                                        ?
                                        <StyledProfileBoldText
                                            onClick={(event) => {
                                                event.stopPropagation();
                                                handleRowDataInput(ERowSateAction.LASTNAME);
                                            }}
                                        >{userData.last_name}</StyledProfileBoldText>
                                        :
                                        <FormikField name="lastname" errorClassName={styles.textFieldError}>
                                            <Textfield
                                                marginLeft="8vw"
                                                autoFocus
                                                type="text"
                                                inputWidth="20rem"
                                                ref={lastnameInputRef}
                                                onKeyDown={event => {
                                                    event.stopPropagation();
                                                    if (event.key === 'Enter') {
                                                        event.preventDefault();
                                                        if (!errors.lastname) {                                                         
                                                            handleRowDataInput(ERowSateAction.LASTNAME);
                                                        }
                                                        if (!rowState.isDisplayedLastName) {
                                                            handleSubmit();
                                                        }
                                                    }
                                                }}
                                            />
                                        </FormikField>
                                }
                                action={
                                    <Button
                                        type="submit"
                                        onClick={(event) => {
                                            event.stopPropagation();
                                            if (!errors.lastname || rowState.isDisplayedLastName) {
                                                handleRowDataInput(ERowSateAction.LASTNAME);
                                            }
                                            if (!rowState.isDisplayedLastName) {
                                                handleSubmit();
                                            }
                                        }}
                                        className={buttonStyles.buttonStyle}
                                    >
                                        {rowState.isDisplayedLastName ? 'Изменить' : 'Сохранить'}
                                    </Button>
                                }
                            />
                            <StyledHR />
                            <CardRow
                                label="Имя"
                                content={
                                    rowState.isDisplayedFirstName
                                        ?
                                        <StyledProfileBoldText
                                            onClick={(event) => {
                                                event.stopPropagation();
                                                handleRowDataInput(ERowSateAction.FIRSTNAME);
                                            }}
                                        >{userData.first_name}</StyledProfileBoldText>
                                        :
                                        <FormikField name="firstname" errorClassName={styles.textFieldError}>
                                            <Textfield
                                                marginLeft="8vw"
                                                autoFocus
                                                type="text"
                                                inputWidth="20rem"
                                                ref={firstnameInputRef}
                                                onKeyDown={event => {
                                                    event.stopPropagation();
                                                    if (event.key === 'Enter') {
                                                        event.preventDefault();
                                                        if (!errors.firstname) {
                                                            handleRowDataInput(ERowSateAction.FIRSTNAME);
                                                        }                                                       
                                                        if (!rowState.isDisplayedFirstName) {
                                                            handleSubmit();
                                                        }
                                                    }
                                                }}
                                            />
                                        </FormikField>
                                }
                                action={
                                    <Button
                                        type="submit"
                                        onClick={(event) => {
                                            event.stopPropagation();
                                            if (!errors.firstname || rowState.isDisplayedFirstName) {
                                                handleRowDataInput(ERowSateAction.FIRSTNAME);
                                            }                                          
                                            if (!rowState.isDisplayedFirstName) {
                                                handleSubmit();
                                            }
                                        }}
                                        className={buttonStyles.buttonStyle}
                                    >
                                        {rowState.isDisplayedFirstName ? 'Изменить' : 'Сохранить'}
                                    </Button>
                                }
                            />
                            <StyledHR />
                            <CardRow
                                label="Отчество"
                                content={
                                    rowState.isDisplayedMiddleName
                                        ?
                                        <StyledProfileBoldText
                                            onClick={(event) => {
                                                event.stopPropagation();
                                                handleRowDataInput(ERowSateAction.MIDDLENAME);
                                            }}
                                        >{userData.middle_name}</StyledProfileBoldText>
                                        :
                                        <FormikField name="middlename" errorClassName={styles.textFieldError}>
                                            <Textfield
                                                marginLeft="8vw"
                                                autoFocus
                                                type="text"
                                                inputWidth="20rem"
                                                ref={middlenameInputRef}
                                                onKeyDown={event => {
                                                    event.stopPropagation();
                                                    if (event.key === 'Enter') {
                                                        event.preventDefault();
                                                        if (!errors.middlename) {
                                                            handleRowDataInput(ERowSateAction.MIDDLENAME);
                                                        }                                                        
                                                        if (!rowState.isDisplayedMiddleName) {
                                                            handleSubmit();
                                                        }
                                                    }
                                                }}
                                            />
                                        </FormikField>
                                }
                                action={
                                    <Button
                                        type="submit"
                                        onClick={(event) => {
                                            event.stopPropagation();
                                            if (!errors.middlename || rowState.isDisplayedMiddleName) {
                                                handleRowDataInput(ERowSateAction.MIDDLENAME);
                                            }                                           
                                            if (!rowState.isDisplayedMiddleName) {
                                                handleSubmit();
                                            }
                                        }}
                                        className={buttonStyles.buttonStyle}
                                    >
                                        {rowState.isDisplayedMiddleName ? 'Изменить' : 'Сохранить'}
                                    </Button>
                                }
                            />
                            <StyledHR />
                            <CardRow
                                label="Адрес электронной почты"
                                content={
                                    rowState.isDisplayedEmail
                                        ?
                                        <StyledProfileBoldText
                                            onClick={(event) => {
                                                event.stopPropagation();
                                                handleRowDataInput(ERowSateAction.EMAIL);
                                            }}
                                        >{userData.email}</StyledProfileBoldText>
                                        :
                                        <FormikField name="email" errorClassName={styles.textFieldError}>
                                            <Textfield
                                                autoFocus
                                                type="text"
                                                inputWidth="20rem"
                                                marginLeft="8vw"
                                                ref={emailInputRef}
                                                onKeyDown={event => {
                                                    event.stopPropagation();
                                                    if (event.key === 'Enter') {
                                                        event.preventDefault();
                                                        if (!errors.email) {
                                                            handleRowDataInput(ERowSateAction.EMAIL);
                                                        }                                                      
                                                        if (!rowState.isDisplayedEmail) {
                                                            handleSubmit();
                                                        }
                                                    }
                                                }}
                                            />
                                        </FormikField>
                                }
                                action={
                                    <Button
                                        type="submit"
                                        onClick={(event) => {
                                            event.stopPropagation();
                                            if (!errors.email || rowState.isDisplayedEmail) {
                                                handleRowDataInput(ERowSateAction.EMAIL);
                                            }
                                            if (!rowState.isDisplayedEmail) {
                                                handleSubmit();
                                            }
                                        }}
                                        className={buttonStyles.buttonStyle}
                                    >
                                        {rowState.isDisplayedEmail ? 'Изменить' : 'Сохранить'}
                                    </Button>
                                }
                            />
                        </Form>
                    )}
                </Formik>
            </ContainerCard>
        </div>
    )
};

export default ProfileCard;